Skip to main content

Station Search

Overview

As of iOS SDK 5.8.0 and Android SDK 7.1.0, the SDKs can search for a station, set it as the active station, and optionally begin loading audio for immediate playback — all in a single network request. This is equivalent to retrieving the station list, picking a station, calling setActiveStation, and then preparing the player, but without the extra round-trips.

This is the recommended way to start playback of First Play and Replay stations, and is especially useful when you want music to start as quickly as possible after a user action.

A search request contains one or more search queries. Each query may specify:

  • type - restrict the match to radio, first_play, or replay stations (or leave unset to match any type)
  • advance (iOS) / at (Android) - the number of seconds into the station at which playback should begin. Only applies to First Play and Replay stations. If a First Play station has already been played past this offset on the device, it will not match. This value is ignored for radio stations.
  • filter - a MongoDB-style filter object tested against station attributes

Queries are evaluated in order until a playable station is found, so you can list your preferred match first and fall back to something more general.

Filter syntax

The filter is a map of logical tests against station attributes:

  • Equality: { "name": "HIIT Workout #1" }
  • Wildcard substring match: { "name": "*Workout*" }
  • Operators $in, $nin, $ne, $exists, $and, and $or: { "class": { "$in": ["hiit", "cardio"] } }

Searching and starting playback

The example below searches for a First Play station whose name contains "Workout", starting playback 8 minutes (480 seconds) in, and falls back to any radio station if there is no match. Because prepareToPlay is true, the player begins buffering audio before the callback fires, so a subsequent play() starts almost instantly.

let queries: [FMStationSearchQuery] = [
.firstPlay(withAdvance: 480, // begin playback 8 minutes in
filter: ["name": "*Workout*"]),

// fall back to any radio station
.radio(withAdvance: 0, filter: nil),
]

let player = FMAudioPlayer.shared()

let dispatched = player.search(
forAndSetActiveStation: queries,
searchTimeoutMs: 2000, // defaults to 2000ms when nil
prepareToPlay: true,
prepareTimeoutMs: 5000 // defaults to 5000ms when nil
) { audioItem, error in
if let error {
// no station matched, or the search or prepare timed out
print("station search failed: \(error)")
return
}

// the station is now active and audio is buffered
player.play()
}

if !dispatched {
// the network is unreachable or the search list was empty;
// the completion block will NOT be invoked
}

The completion block is invoked exactly once, on the main queue. Possible errors include FeedFMErrorCodeRequestTimeout, FeedFMErrorCodePlayerCouldNotSetStation, and FeedFMErrorCodePlayerPrepareTimeout — see SDK Error Handling. If the prepare timeout is exceeded, the station and audio item are still set as active and audio continues loading in the background.